﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;
using System.IO;

//Mark Rowe 
//From the IRDev Library Project.

namespace AutoDeploy.Common.Library
{
    class XPathHelper
    {
        public static XElement AddElementFromXPath(XElement x, string xPathExpression, string valueToAdd, List<string> ls)
        {
            List<string> lst = new List<string>();
            XmlReader reader = XmlReader.Create(new StringReader(x.ToString()));
            XElement root = XElement.Load(reader);
            XmlNameTable nameTable = reader.NameTable;
            XmlNamespaceManager nsm = new XmlNamespaceManager(nameTable);
            foreach (string t in ls)
            {
                lst = StringHelper.StringToListOfString(t, "::", true, false);
                nsm.AddNamespace(lst[0], lst[1]);
            }
            return AddElementToElementUsingXPath(x, xPathExpression, valueToAdd, nsm);
        }
        public static XElement AddElementFromXPath(XElement x, string xPathExpression, string valueToAdd)
        {
            List<string> ls = new List<string>();
            return AddElementFromXPath(x, xPathExpression, valueToAdd, ls);
        }
        public static XElement EditAttributeInXPath(XElement x, string xPathExpression, string valueToAdd, List<string> ls)
        {
            List<string> lst = new List<string>();
            XmlReader reader = XmlReader.Create(new StringReader(x.ToString()));
            XElement root = XElement.Load(reader);
            XmlNameTable nameTable = reader.NameTable;
            XmlNamespaceManager nsm = new XmlNamespaceManager(nameTable);
            foreach (string t in ls)
            {
                lst = StringHelper.StringToListOfString(t, "::", true, false);
                nsm.AddNamespace(lst[0], lst[1]);
            }
            return EditAttributeInXPath(x, xPathExpression, nsm, valueToAdd);
        }
        public static XElement EditAttributeInXPath(XElement x, string xPath, XmlNamespaceManager nsm, string valueToAdd)
        {
            if (xPath.Contains("/"))
            {
                bool useLinqStyleXPath = !xPath.Contains("local-name");
                string xPathForLinq = (useLinqStyleXPath ? xPath : CreatedFormattedLinqXPathFromInstanceXPath(xPath, nsm));
                string xElementContainerXPath = xPathForLinq.Substring(0, xPathForLinq.LastIndexOf("/") - 1);
                string xAttributeName = xPathForLinq.Substring(xPathForLinq.LastIndexOf("/") + 1).Replace("@", "");
                x.XPathSelectElement(xElementContainerXPath, nsm).Attribute(xAttributeName).Value = valueToAdd;
            }
            return x;
        }
        public static string EditAttributeInXPath(string uri, string xPath, string valueToAdd, List<string> ls)
        {
            XElement xe = XElement.Load(uri);
            return EditAttributeInXPath(xe,xPath,valueToAdd,ls).ToString();
        }
        public static XElement ReplaceOrAddIfMissingElementInElementUsingXPath(XElement x, string xPathExpression, string valueToAdd, List<string> ls)
        {
            List<string> lst = new List<string>();
            XmlReader reader = XmlReader.Create(new StringReader(x.ToString()));
            XElement root = XElement.Load(reader);
            XmlNameTable nameTable = reader.NameTable;
            XmlNamespaceManager nsm = new XmlNamespaceManager(nameTable);
            foreach (string t in ls)
            {
                lst = StringHelper.StringToListOfString(t, "::", true, false);
                nsm.AddNamespace(lst[0], lst[1]);
            }
            return ReplaceOrAddIfMissingElementInElementUsingXPath(x, xPathExpression, nsm, valueToAdd);
        }
        public static XElement ReplaceOrAddIfMissingElementInElementUsingXPath(XElement x, string xPath,XmlNamespaceManager nsm, string xmlToAdd)
        {
            bool useLinqStyleXPath = !xPath.Contains("local-name");
            string xPathForLinq = (useLinqStyleXPath ? xPath : CreatedFormattedLinqXPathFromInstanceXPath(xPath, nsm));
            if (x.XPathSelectElements(xPathForLinq, nsm).Count() > 0)
            {
                x.XPathSelectElement(xPathForLinq, nsm).Value = xmlToAdd;
            }
            else
            {
                return AddElementToElementUsingXPath(x, xPathForLinq, xmlToAdd, nsm);
            }
            return x;
        }
        public static XElement AddElementToElementUsingXPath(XElement x, string xPath, string xmlToAdd, XmlNamespaceManager nsm)
        {
            bool useLinqStyleXPath = !xPath.Contains("local-name");
            List<string> ls = (useLinqStyleXPath ? StringHelper.StringToListOfString(xPath, "/", true, true) : StringHelper.StringToListOfString(xPath, "/*", true, true));
            ls.Reverse();
            List<ElementSet> les = new List<ElementSet>();
            string currentPath = string.Empty;
            XElement xe;
            ElementSet es;
            string xPathForLinq = (useLinqStyleXPath ? xPath : CreatedFormattedLinqXPathFromInstanceXPath(xPath, nsm));
            string reparse = string.Empty;
            foreach (string s in ls)
            {
                currentPath = (useLinqStyleXPath ? "/*" + CreateFormattedInstanceElementFromLinqXPath(s, nsm) : "/*" + s);
                es = new ElementSet(currentPath);
                if (x.XPathSelectElements(xPathForLinq, nsm).Count() > 0)
                {
                    xe = XElement.Parse(xmlToAdd.Replace(":", "___"));
                    x.XPathSelectElement(xPathForLinq, nsm).Add(CreateXElementFromNameList(xe, les));
                    reparse = x.ToString().Replace("___", ":");
                    x = XElement.Parse(reparse);
                    return x;
                }
                else
                {
                    currentPath = "/" + s;
                    es = new ElementSet(currentPath);
                    les.Add(es);
                }
            }
            return x;
        }
        public static string CreateFormattedInstanceElementFromLinqXPath(string LinqXPath, XmlNamespaceManager nsm)
        {
            List<string> ls = StringHelper.StringToListOfString(LinqXPath, ":", true, true);
            return (ls.Count > 1 ? "[local-name()='" + ls[1] + "' and namespace-uri()='" + nsm.LookupNamespace(ls[0]) + "']" : "[local-name()='" + ls[1] + "' and namespace-uri()='']");
        }
        private static string CreatedFormattedLinqXPathFromInstanceXPath(string xPath, XmlNamespaceManager nsm)
        {
            ElementSet es;
            StringBuilder sb = new StringBuilder(".");
            foreach (string ss in StringHelper.StringToListOfString(xPath, "/*", true, true))
            {
                es = new ElementSet("/*" + ss);
                if (es.CurrentElement.Contains(":"))
                {
                    sb.Append("/" + es.CurrentElement);
                }
                else
                {
                    sb.Append("/" + (es.hasNameSpace ? nsm.LookupPrefix(es.NameSpace) + ":" : string.Empty) + es.CurrentElement);
                }
            }
            return sb.ToString();
        }
        public static XElement CreateXElementFromNameList(XElement x, List<ElementSet> les)
        {
            XElement xe = x;
            foreach (ElementSet es in les)
            {
                if (es.hasNameSpace)
                {
                    x = (es.hasNameSpace ? new XElement(es.xNameSpace + es.CurrentElement) : new XElement(es.CurrentElement));
                }
                x.Add(xe);
            }
            return x;
        }
        public static XElement CreateXElementFromXPath(XElement x, string xPath)
        {
            XElement xeNew = null;
            XElement xeBase = null;
            List<string> ls =StringHelper.StringToListOfString(xPath, "/*", true, true);
            ls.Reverse();
            foreach (string s in ls)
            {
                if (xeNew != null)
                {
                    xeBase = xeNew;
                    //If it isn't the element to Add
                    ElementSet es = new ElementSet("/*" + s);
                    if (es.hasNameSpace)
                    {
                        xeNew = new XElement(es.xNameSpace + es.CurrentElement);
                    }
                    else
                    {
                        xeNew = new XElement(es.CurrentElement);
                    }
                    xeNew.Add(xeBase);
                }
                else
                {
                    ElementSet es = new ElementSet("/*" + s);
                    if (es.hasNameSpace)
                    {
                        xeNew = new XElement(es.xNameSpace + es.CurrentElement);
                    }
                    else
                    {
                        xeNew = new XElement(es.CurrentElement);
                    }
                }
            }
            x.Add(xeNew);
            return x;
        }
        public static string GetXNameSpaceNameLocal(string xPath)
        {
            if (xPath.Contains("/*") && xPath.Contains("'") && xPath.Contains(" and "))
            {
                if (xPath.IndexOf("/*") != xPath.LastIndexOf("/*"))
                {
                    return "";
                }
                else
                {
                    return xPath.Substring(xPath.LastIndexOf("=") + 1, xPath.LastIndexOf("'") - (xPath.LastIndexOf("=") + 1)).Replace("'", "").Trim();
                }
            }
            else
            {
                return "";
            }
        }
        public static string GetElementNameLocal(string xPath)
        {
            if (xPath.Contains("/*") && xPath.Contains("=") && xPath.Contains("'") && xPath.Contains("uri"))
            {
                if (xPath.IndexOf("/*") != xPath.LastIndexOf("/*"))
                {
                    return "MORE_THAN_1_ELEMENT_FOUND";
                }
                else
                {
                    return xPath.Substring(xPath.IndexOf("'") + 1, xPath.IndexOf(" and ") - (xPath.IndexOf("'") + 1)).Replace("'", "").Trim();
                }
            }
            else
            {
                return "IMPROPERLY_FORMED_XPATH";
            }
        }

        public static string AddElementFromXPath(string xElementUri, string xPathExpression, string valueToAdd, List<string> namespaces)
        {
            XElement xe = XElement.Load(xElementUri);
            return AddElementFromXPath(xe, xPathExpression, valueToAdd, namespaces).ToString();
        }

        internal static string ReplaceOrAddIfMissingElementInElementUsingXPath(string xElementUri, string xPathExpression, string valueToAdd, List<string> namespaces)
        {
            XElement xe = XElement.Load(xElementUri);
            return ReplaceOrAddIfMissingElementInElementUsingXPath(xe, xPathExpression, valueToAdd, namespaces).ToString();
        }
    }
}
